﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace BFlatCompile.Nodes
{
	public abstract class Executable : ICodeContainer
	{
		public static readonly Executable[] Noop = new Executable[0];

		protected Executable()
		{
		}

		public static Executable[] AppendReturnIfNecessary(IList<Executable> lines)
		{
			if (lines.Count == 0 || !(lines[lines.Count - 1] is Return))
			{
				lines.Add(new Return(NullConstant.Instance));
			}

			return lines.ToArray();
		}

		public static Executable WrapExpressionAsExecutable(Expression expression)
		{
			return new ExpressionAsExecutable(expression);
		}

		public static Executable Parse(Tokens tokens, bool semicolonPresent, bool allowComplex)
		{
			string first = tokens.Peek();

			if (
				//tokens.IsBuiltIn && 
				first == "argmatch")
			{
				return ArgMatch.ParseArgMatch(tokens);
			}

			switch (first)
			{
				case "for":
					if (!allowComplex) throw new ParserException("For loop is not allowed here.", tokens.PopInstance());
					return Loop.ParseFor(tokens);

				case "while":
					if (!allowComplex) throw new ParserException("While loop is not allowed here.", tokens.PopInstance());
					return Loop.ParseWhile(tokens);

				case "do":
					if (!allowComplex) throw new ParserException("Do While loop is not allowed here.", tokens.PopInstance());
					return Loop.ParseDoWhile(tokens);

				case "if":
					if (!allowComplex) throw new ParserException("If statement is not allowed here.", tokens.PopInstance());
					return IfStatement.ParseIf(tokens);

				case "try":
					if (!allowComplex) throw new ParserException("Try block is not allowed here.", tokens.PopInstance());
					return TryBlock.Parse(tokens);

				case "switch":
					if (!allowComplex) throw new ParserException("Switch statement is not allowed here.", tokens.PopInstance());
					return Switch.Parse(tokens);

				case "foreach":
					if (!allowComplex) throw new ParserException("Foreach loop is not allowed here.", tokens.PopInstance());
					return ForeachLoop.ParseForeach(tokens);

				case "return":
					if (!allowComplex) throw new ParserException("Return is not allowed here.", tokens.PopInstance());
					return Return.ParseReturn(tokens);

				case "break":
					if (!allowComplex) throw new ParserException("Break is not allowed here.", tokens.PopInstance());
					return Break.ParseBreak(tokens);

				case "continue":
					if (!allowComplex) throw new ParserException("Continue is not allowed here.", tokens.PopInstance());
					return Continue.ParseContinue(tokens);

				case "throw":
					if (!allowComplex) throw new ParserException("Throw is not allowed here.", tokens.PopInstance());
					return Throw.ParseThrow(tokens);

				default:
					// +=  -=  *=  etc.
					Expression expression = Expression.Parse(tokens);
					Executable executable;
					char op = '\0';
					Token token = null;
					if (tokens.PopIfPresent("+=")) { op = '+'; token = tokens.PreviousToken(); }
					else if (tokens.PopIfPresent("-=")) { op = '-'; token = tokens.PreviousToken(); }
					else if (tokens.PopIfPresent("*=")) { op = '*'; token = tokens.PreviousToken(); }
					else if (tokens.PopIfPresent("/=")) { op = '/'; token = tokens.PreviousToken(); }
					else if (tokens.PopIfPresent("|=")) { op = '|'; token = tokens.PreviousToken(); }
					else if (tokens.PopIfPresent("&=")) { op = '&'; token = tokens.PreviousToken(); }
					else if (tokens.PopIfPresent("^=")) { op = '^'; token = tokens.PreviousToken(); }
					else if (tokens.PopIfPresent("=")) { op = '='; token = tokens.PreviousToken(); }

					if (op != '\0')
					{
						Expression modifier = Expression.Parse(tokens);
						// TODO: test this when the value cannot be dereferenced e.g. foo['non-existent key'] += 1;
						executable = new Assignment(expression, modifier, op, token);
					}
					else
					{
						executable = Executable.WrapExpressionAsExecutable(expression);
					}

					if (semicolonPresent)
					{
						tokens.PopExpected(";");
					}

					return executable;
			}
		}

		public abstract void ResolveCode(Class c, Dictionary<object, object> parents);
		public abstract void ResolveClassToThis(Class c);
	}
}
